package utilities;

import exception.ExtraCharactersException;
import exception.IllegalCharacterSequenceException;
import exception.LessCharactersExeption;
import exception.NoRuleException;
import exception.UninitializedVariableException;
import guard_grammar.Evaluation;
import guard_grammar.RestrictionEvaluation;

import java.io.IOException;
import java.util.LinkedList;

import net.sourceforge.jeval.EvaluationException;

import model.EdgeProgramGraph;
import model.NodeUnfoldingTransitionSystem;
import model.ProgramGraph;
import model.UnfoldingTransitionSystem;
import model.VariableHashTable;

/** Class for simulation of the algorithm for supervisor creator for deterministic/non-deterministic graph with variables
 * @author DarkoLaptop2
 *
 */
public class VSupervisorCreator {
	
	/** Method for checking if all the variables are in the domain for a given graph
	 * @param graph - the graph containing the variables domain
	 * @param currentValues - the current values of the variables
	 * @return true/false
	 */
	private static boolean areVariablesInDomain(ProgramGraph graph,VariableHashTable currentValues){
		for(String variableName:currentValues.getAllVariables()){
			if (!graph.checkVariableInDomain(variableName, currentValues.getVariableValue(variableName)))
				return false;
		}
		return true;
	}
	
	/** Method for creation of a supervisor by unfolding a given graph with variables
	 * @param graph - the  graph with variables
	 * @return  - a resulting graph without variables representing the supervisor
	 */
	public static UnfoldingTransitionSystem createSupervisor(ProgramGraph graph) throws EvaluationException, UninitializedVariableException, IOException, IllegalCharacterSequenceException, LessCharactersExeption, ExtraCharactersException, NoRuleException{
		UnfoldingTransitionSystem supervisor=new UnfoldingTransitionSystem();
		supervisor.addNode(0, graph.getInitialNode().getName(),graph.getInitialVariableValues());
		RestrictionEvaluation restrictionEvaluator=new RestrictionEvaluation(graph.getRestrictionFilePath());
		restrictionEvaluator.checkRestriction(graph);
		int index=1;
		LinkedList<NodeUnfoldingTransitionSystem> queue=new LinkedList<NodeUnfoldingTransitionSystem>();
		queue.addLast(new NodeUnfoldingTransitionSystem(0, graph.getInitialNode().getName(), graph.getInitialVariableValues()));
		while(!queue.isEmpty()){
			NodeUnfoldingTransitionSystem currentState=queue.removeFirst();
			int indexState=graph.getNodeIndex(currentState.getName());
			for(EdgeProgramGraph transition:graph.getNodeNeighbors(indexState)){
				if(Evaluation.evaluateGuard(transition.getGuardString(), currentState.getVariablesTable())){
					VariableHashTable updatedTable=currentState.getVariablesTable().copyHashTable();
					Evaluation.evaluateUpdate(transition.getActionString(), updatedTable);
					if(areVariablesInDomain(graph,updatedTable)){
						boolean areRestrictionsFalse=true;
						for(String restriction:graph.getTransitionRestrictions(transition.getTransitionName()))
							if(Evaluation.evaluateGuard(restriction, updatedTable)){
								areRestrictionsFalse=false;
								break;
							}
						if(areRestrictionsFalse){
							int index2=supervisor.getNodeIndex(transition.getToNode().getName(),updatedTable);
							if(index2==-1){
								NodeUnfoldingTransitionSystem newNode=new NodeUnfoldingTransitionSystem(index, transition.getToNode().getName(), updatedTable.copyHashTable());
								supervisor.addNode(index, transition.getToNode().getName(), updatedTable.copyHashTable());
								supervisor.addEdge(currentState.getIndex(), transition.getTransitionName(), index);
								queue.addLast(newNode);
								index++;
							}else {
								supervisor.addEdge(currentState.getIndex(), transition.getTransitionName(), index2);
							}
						}
					}
					
				}
			}
		}
		supervisor.setInitialNode(0);
		return supervisor;
	}
}
